# 存一些通用方法
import time
from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys
from common.loggeManager import loggerM
from common.configManager import local_config
import os
from config import DIR_PATH

class BaseMethod:
    '''
     #初始化方法：主要实现浏览器窗口最大化，需要传入的参数：driver
       # driver: 创建好的浏览器对象
    '''
    def __init__(self,driver):
        '''
        :param driver: 传入一个打开的浏览器
        '''
        # 将传入的参数driver，赋值给实例变量，以便其它方法可以调用这个变量
        self.driver = driver
        #浏览器窗口最大化和隐式等待
        self.driver.maximize_window()
        self.driver.implicitly_wait(local_config.time_out())
        loggerM.info("浏览器初始化成功")


    # ------------------打开指定的测试地址------------------------------
    def open(self,url):
        '''
        :param url: 测试地址
        :return:
        '''
        try:
            self.driver.get(url)
            loggerM.info("正在打开网址：{}".format(url))
        except:
            self.get_screen()
            loggerM.error("打开网页{}时失败".format(url))
            raise

    #------------------根据指定的方式获取元素------------------------------
    '''
        根据指定的方式获取元素
        #by		：定位方式
        #locate	：不同定位方式的表达式

        by可以传入以下参数
        id：使用id属性定位元素
        name：使用name属性定位元素
        xpath：使用xpath定位元素
        link text：使用超链接文本定位元素
        partial link text：使用部分链接文本定位元素
        css selector：使用CSS选择器定位元素
        class name：使用class属性定位元素
        tag name：使用标签名定位
        举例： 假如by的值id，那么locate则应该是一个元素的id值。
    '''

    def get_element(self, by, locate):
        '''
        :param by: 定位方式
        :param locate: 定位表达式
        :return:
        '''
        loggerM.info("正在定位元素：({},{})".format(by, locate))
        list2 = ["id", "xpath", "link text", "partial link text", "name", "tag name", "class name", "css selector"]
        elem = None
        if by in list2:
            try:
                elem = self.driver.find_element(by, locate)
            except:
                self.get_screen()
                loggerM.error("元素定位失败：定位方式为{}，定位表达式为{}".format(by,locate))
                raise
        else:
            loggerM.info("您输入的by类型错误,只支持:css selector、id、name、tag name、xpath、link text、partial link text、class name")
            # 如果传入的参数不符合要求，则提示错误信息
            raise Exception(
                "您输入的by类型错误,只支持:css selector、id、name、tag name、xpath、link text、partial link text、class name")
        return elem

    # --------------获取屏幕截图----------------------------------
    def get_screen(self):
        str_time = time.strftime("%Y%m%d%H%M%S")  # 获取当前的时间字符串
        # file_name = "../image/" + str_time + ".png"  # 指定截图文件的路径和名称
        file_name=os.path.join(DIR_PATH,local_config.image_path(),str_time+".png")
        self.driver.get_screenshot_as_file(file_name)  # 执行截图的操作
        loggerM.info("正在执行截图的操作！")

    #-----------------往输入框中输入内容-------------------------------
    ''' 
        该方法的作用：往一个输入框中，输入指定的内容
        by: 选择一种定位方式
        locate: 对应的定位表达式
        value: 需要输入的内容
    '''
    def inputKeys(self,by,locate,value):
        '''
        :param by: 定位方式
        :param locate: 定位表达式
        :param value: 输入的内容
        :return:
        '''
        try:
            elem = self.get_element(by,locate)  # 调用get_element方法，定位输入框元素
            elem.clear()
            elem.send_keys(value)  # 往输入框中输入内容
            loggerM.info("成功往输入框({},{})中输入：{}".format(by,locate,value))
        except:
            self.get_screen()
            loggerM.error("往输入框({},{})中输入：{}失败".format(by,locate,value))
            raise

    # -----------------点击某个按钮的操作------------------------------
    def click(self,by,locate):
        '''
        :param by: 定位方式
        :param locate: 定位表达式
        :return:
        '''
        try:
            self.get_element(by,locate).click()
            loggerM.info("成功往点击按钮({},{})".format(by,locate))
        except:
            self.get_screen()
            loggerM.error("点击按钮({},{})失败".format(by,locate))
            raise

# -----------------封装登录的操作-------------------------------
    def ht_login(self,name,passwd):
        '''
        :param name: 用户名
        :param passwd: 密码
        :return:
        '''
        loggerM.info("正在执行登录操作，用户名：{}，密码：{}".format(name,passwd))
        self.inputKeys("name","admin_name",name)
        self.inputKeys("name","password",passwd)
        self.inputKeys("name", "captcha", "123456")
        self.click("class name","submit")
        loggerM.info("登录成功，用户名：{}，密码：{}".format(name, passwd))

    # -----------------列表定位-------------------------------
    def get_elements(self, by, locate):
        '''
        :param by: 定位方式
        :param locate: 定位表达式
        :return: 返回一个列表
        '''
        list2 = ["id", "xpath", "link text", "partial link text", "name", "tag name", "class name", "css selector"]
        elems = []
        if by in list2:
            elems = self.driver.find_elements(by, locate)
            if elems==[]:
                self.get_screen()
                loggerM.error("列表定位：通过({},{})无法定位到元素".format(by, locate))
                raise
            else:
                loggerM.info("列表定位：通过({},{})成功定位到元素".format(by, locate))
        else:
            loggerM.info("您输入的定位方式关键字错误，只支持：css selector、id、name、tag name、xpath、link text、partial link text、class name")
            # 如果传入的参数不符合要求，则提示错误信息
            raise Exception(
                "您输入的by类型错误,只支持:css selector、id、name、tag name、xpath、link text、partial link text、class name")
        return elems

    def mouse_hove(self,by,locate):
        '''
        :param by: 定位方式
        :param locate: 定位表达式
        :return:
        '''
        # 创建一个鼠标对象  -- 鼠标工具
        loggerM.info("正在执行鼠标悬浮的操作！！！，悬浮元素：({},{})".format(by,locate))
        try:
            elem = self.get_element(by,locate)
            action = ActionChains(self.driver)
            action.move_to_element(elem).perform()
            time.sleep(1)
        except:
            loggerM.info("鼠标悬浮的操作失败，悬浮元素：({},{})".format(by,locate))
            self.get_screen()
            raise

    # ---------------------键盘回车键的操作------------------------------------
    ''' 
      该方法的作用：使用键盘回车键，执行回车操作
      by: 选择一种定位方式
      locate: 对应的定位表达式
    '''
    def enter(self,by,locate):
        '''
        :param by: 定位方式
        :param locate: 定位表达式
        :return:
        '''
        loggerM.info("正在使用键盘执行回车操作！！")
        elem = self.get_element(by,locate) #根据传入的loacator获取对应的元素
        elem.send_keys(Keys.ENTER) #执行回车操作

    def ctrl_x(self,by,locate):
        '''
        :param by: 定位方式
        :param locate: 定位表达式
        :return:
        '''
        loggerM.info("正在使用键盘执行剪切操作！！")
        elem = self.get_element(by, locate)  # 根据传入的loacator获取对应的元素
        elem.send_keys(Keys.CONTROL,"a")  # 执行全选的操作
        elem.send_keys(Keys.CONTROL,"x")  # 执行剪切的操作

    # ----------------------frame表单的切换------------------------------------
    ''' 
      该方法的作用：执行切换frame表单的操作
      by: 选择一种定位方式
      locate: 对应的定位表达式
    '''

    def switch_frame(self,by,locate):
        '''
        :param by: 定位方式
        :param locate: 定位表达式
        :return:
        '''
        try:
            loggerM.info("正在执行表单切换的操作，表单元素：({},{})".format(by,locate))
            elem = self.get_element(by,locate)  #获取对应的frame表单元素
            self.driver.switch_to.frame(elem)  #执行切换的操作
        except:
            loggerM.error("表单切换失败，表单元素：({},{})".format(by,locate))
            self.get_screen()
            raise

        # ----------------------滚动条的处理------------------------------------
        ''' 
          该方法的作用：循环执行滚动条操作，直到找到想要的元素
          by: 选择一种定位方式
          locate: 对应的定位表达式
        '''

    def click_elem_by_scrollTo(self, by, locate):
        '''
        :param by: 定位方式
        :param locate: 定位表达式
        :return:
        '''
        elem = []
        for i in range(10):  # 最多下拉10次，次数可以修改。注意不能太多，多了影响性能
            m = i * 1000 + 1000  # 定义一个变量，用于获取滚动条下拉的距离
            js = "window.scrollTo(0," + str(m) + ")"  # 执行滚动操作的Js代码
            try:
                time.sleep(0.5)  # 等待0.5秒，让页面加载后再去获取元素
                elem = self.get_element(by, locate)  # 获取元素
                elem.click()
            except Exception:
                self.driver.execute_script(js)  # 执行滚动条的操作
            else:
                break

    def input_elem_by_scrollTo(self, by, locate,value):
        '''
        使用滚动条找到元素后在输入内容
        :param by:  定位方式
        :param locate: 定位表达式
        :param value: 输入的内容
        :return:
        '''
        elem = []
        for i in range(10):  # 最多下拉10次，次数可以修改。注意不能太多，多了影响性能
            m = i * 1000 + 1000  # 定义一个变量，用于获取滚动条下拉的距离
            js = "window.scrollTo(0," + str(m) + ")"  # 执行滚动操作的Js代码
            try:
                time.sleep(0.5)  # 等待0.5秒，让页面加载后再去获取元素
                elem = self.get_element(by, locate)  # 获取元素
                elem.send_keys(value)
            except Exception:
                self.driver.execute_script(js)  # 执行滚动条的操作
            else:
                break

    #---------------------窗口的切换---------------------------
    def switch_window(self):
        loggerM.info("正在执行切换句柄操作！")
        try:
            cur_handle = self.driver.current_window_handle   #获取当前窗口的句柄
            handles = self.driver.window_handles  #获取所有窗口的句柄，返回一个列表

            #遍历列表的方式实现窗口切换
            for handle in handles:  #使用for循环遍历切换窗口
                if handle != cur_handle:
                    #判断，如果handle和当前句柄cur_handle不相同，表示窗口是新打开的窗口
                    self.driver.switch_to.window(handle)
        except:
            loggerM.error("切换句柄失败")
            self.get_screen()
            raise


    # -------------弹框的处理方法------------------------
    '''
        定义一个方法，执行windows弹框的操作，参数type的取值：
        type:
            默认值是accept，表示执行确认操作。
            当值为dismiss时忽略的操作
            当值为text时，表示获取弹框的文本内容
    '''
    def alert_oper(self,type="accept"):
        '''
        可以选择需要执行的操作，type只能是dismiss、text或accept，默认值是：accept
        :param type:
        :return:
        '''
        loggerM.info("正在执行")
        text = None  #定义一个变量用于接收文本内容
        alert = self.driver.switch_to.alert  #获取弹框处理工具

        if type == "dismiss": #当type等于dismiss时，执行取消的操作
            alert.dismiss()
        elif type == "accept": #当type等于accept时，执行确认的操作
            alert.accept()
        elif type == "text": #当type等于text时，则获取弹框中的文本内容
            text = alert.text
        else:
            loggerM.error("您输入的type类型不对，只支持：accept、dismiss、和text")
            #输入的type类型不对时，弹出提示信息
            raise Exception("您输入的type类型不对，只支持：accept、dismiss、和text")
        return text

    # -------------获取浏览器标题--------------------------------------
    def get_title(self):
        loggerM.info("正在获取浏览器的标题！！")
        title = self.driver.title
        return title

    # -------------获取浏览器当前页面的url地址--------------------------
    def get_url(self):
        loggerM.info("正在获取当前页面的url地址！！")
        url = self.driver.current_url
        return url

    # -------------获取浏览器当前页面的源代码--------------------------
    def get_page_source(self):
        time.sleep(1)
        source = self.driver.page_source
        loggerM.info("获取页面源码,用来检查是否包含某个元素：%s" % source)
        return source

    # -------------获取元素的文本值-------------------------
    def get_elem_text(self,by,locate):
        loggerM.info("正在获取元素的文本值")
        elem = self.get_element(by, locate)
        text = elem.text
        loggerM.info("获取文本值成功：%s",text)
        return text

    # -------------获取元素的属性值-------------------------
    def get_elem_attr(self,by,locate,attr):
        '''
        获取元素的属性：
        :param by: 定位方式
        :param locate: 定位表达式
        :param attr: 属性
        :return:
        '''
        loggerM.info("正在获取元素的属性: %s",attr)
        elem = self.get_element(by,locate)
        text = None
        try:
            text = elem.get_attribute(attr)
        except:
            loggerM.error("获取属性：%s失败",attr)
            self.get_screen()
            raise
        loggerM.info("获取属性成功，%s属性的值为：%s",attr,text)
        return text

    # -------------断言_包含-------------------------
    def assert_in(self,hope,actual):
        '''
        根据实际结果中是否包含了预期结果，进行断言
        :param hope: 预期结果
        :param actual: 实际结果
        :return:
        '''
        loggerM.info("正在执行断言操作，判断实际结果：%s，中是否包含了预期结果：%s",actual,hope)
        try:
            assert hope in actual
        except:
            loggerM.error("断言失败：实际结果：%s，中没有包含了预期结果：%s",actual,hope)
            self.get_screen()
            raise
        else:
            loggerM.info("断言成功：实际结果：%s，中包含了预期结果：%s",actual,hope)

    # -------------断言_相等-------------------------
    def assert_equal(self,hope,actual):
        '''
        根据实际结果是否等于预期结果进行断言
        :param hope: 预期结果
        :param actual: 实际结果
        :return:
        '''
        loggerM.info("正在执行断言操作，判断实际结果：%s，是否等于预期结果：%s",actual,hope)
        try:
            assert hope == actual
        except:
            loggerM.error("断言失败：实际结果：%s，不等于预期结果：%s",actual,hope)
            self.get_screen()
            raise
        else:
            loggerM.info("断言成功：实际结果：%s，等于预期结果：%s",actual,hope)

if __name__ == '__main__':
    from selenium import webdriver
    driver = webdriver.Chrome()
    bm = BaseMethod(driver)
    # bm.get_screen()
    print(bm.get_url())
    print(bm.get_page_source())
    driver.find_element_by_id("d")









